/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package utn.tsb.tej.world_logic;

import java.io.Serializable;
import utn.tsb.tej.world_logic.Player;
import csm.uttils.generics.Roster;

/**
 *
 * @author Gaston M. Quiroga
 */
public class World implements Serializable {

    private Roster<Round> rounds;
    private Roster<Continent> continents;
    private Roster<Continent> continentCards;
    private Roster<Country> countryCards;
    private Roster<Situation> situations;
    private Roster<Player> startingPlayers;
    private Roster<ObjectiveCard> objectives;

    public Roster<ObjectiveCard> getObjectives() {
        return objectives;
    }

    public Roster<Continent> getContinentCards() {
        return continentCards;
    }
    private WorldState state;
    private boolean firstRound;

    public World(Roster<Player> players, Roster<Continent> continents, Roster<Situation> situations, Roster<ObjectiveCard> objectives) {
        this.rounds = new Roster<Round>();
        this.continents = continents;
        this.situations = situations;
        this.state = new WorldState(WorldState.CREATED);
        this.startingPlayers = players;
        this.objectives = objectives;
        this.firstRound = true;
        continentCards = (Roster<Continent>) cloneRoster(continents);
        countryCards = getCountriesFromContinents(continentCards);
    }

    /**
     * starts a game in the first round, assigning countries and objectives to
     * each player
     * @param players
     * @param countries
     * @param objectives
     */
    public void newGame() {
        Roster<Country> countries = getCountriesFromContinents(continents);
        unsortPlayers(startingPlayers); //unsorting before asign countries and objectives
        assignCountries(startingPlayers, countries);
        assignObjectives(objectives, startingPlayers);
        rounds.clear();
        unsortPlayers(startingPlayers); //unsorting the order of the turns in the fsr round;
        Round r = new Round(startingPlayers);
        rounds.addLast(r);
        this.state = new WorldState(WorldState.STARTED);
    }

    private Situation getRandomSituation() {
        Situation situation = null;
        int position;
        boolean valid = false;
        while (!valid) {
            position = (int) (Math.random() * situations.count());
            situation = situations.remove(situations.getElementInPosition(position));
            valid = situation.init(rounds.getLast().getTurns());
        }

        return situation;
    }

    Roster<Player> getPlayers() {
        return startingPlayers;
    }

    public Roster<Continent> getContinents() {
        return continents;
    }

    public void setContinents(Roster<Continent> continents) {
        this.continents = continents;
    }

    public Roster<Round> getRounds() {
        return rounds;
    }

    public void setRounds(Roster<Round> rounds) {
        this.rounds = rounds;
    }

    public Roster<Situation> getSituations() {
        return situations;
    }

    public void setSituations(Roster<Situation> situations) {
        this.situations = situations;
    }

    public WorldState getState() {
        return state;
    }

    public void setState(WorldState state) {
        this.state = state;
    }

    public void nextRound(boolean reorder, boolean drawSituation) {
        Round newRound;
        Round r = (Round) rounds.getLast();
        Roster<Player> turns = r.getTurns();//it may be necessary  clone this roster
        if (reorder) {
            Player p = (Player) turns.removeLast();
            turns.addFirst(p);
        }
        if (drawSituation) {
            newRound = new Round(turns, getRandomSituation());
        } else {
            newRound = new Round(turns);
        }

        rounds.addLast(newRound);
    }

    /**
     * This method receives a list of players and assign them one by one the
     * countries included in the list of countries. If the number of countries
     * is not a multiple of the number of players, it takes into account the
     * order of the players to allocate surplus countries. It also assign an army
     * to each country
     * @param players Players who will recieve the countries
     * @param countries Countries to assign
     * @return A roster of players whith his countries and one army in each country.
     */
    private void assignCountries(Roster<Player> players, Roster<Country> countries) {
        int totalOfCountries = countries.count();


        int totalOfPlayers = players.count();


        int assignPlayer = 0;


        for (int i = 0; i
                < totalOfCountries; i++) {
            if (assignPlayer == totalOfPlayers) {
                assignPlayer = 0;


            }
            int random = (int) (Math.random() * countries.count());
            Country countryToAssign = countries.getElementInPosition(random);
            countries.remove(countryToAssign);
            countryToAssign.setArmies(1);
            players.getElementInPosition(assignPlayer).getCountries().addLast(countryToAssign);
            assignPlayer++;

        }


    }

    /**
     * This method assign the bjectives of one ObjectiveCard to each player
     * @param objectives Roster of ObjectiveCards to be assign
     * @param players Players who will receive the objectives
     * @return a list of players whit objectives
     */
    private void assignObjectives(Roster<ObjectiveCard> objectiveCards, Roster<Player> players) {
        int random;


        for (int i = 0; i
                < players.count(); i++) {
            random = (int) (Math.random() * objectiveCards.count());
            ObjectiveCard objectiveCardToAssign = objectiveCards.getElementInPosition(random);
            objectiveCards.remove(objectiveCardToAssign);
            players.getElementInPosition(i).setObjectives(objectiveCardToAssign.getObjectives(players, players.getElementInPosition(i)));


        }
    }

    private void unsortPlayers(Roster<Player> players) {
        Roster<Player> aux = new Roster<Player>();


        int numberOfPlayers = players.count();


        int random;


        for (int i = 0; i
                < numberOfPlayers; i++) {
            random = (int) (Math.random() * players.count());
            Player playerToAdd = players.getElementInPosition(random);
            aux.addLast(players.remove(playerToAdd));


        }
        for (int i = 0; i
                < aux.count(); i++) {
            players.addFirst(aux.getElementInPosition(i));


        }
    }

    public void removePlayer(Player player) {
        rounds.getLast().getTurns().remove(player);


        if (rounds.getLast().getTurns().count() == 1) {
            endGame();


        }
    }

    public void transferCountry(Player newOwner, Country country) {
        country.getOwner().getCountries().remove(country);
        newOwner.getCountries().addLast(country);


    }

    /**
     * Validates that the list of cards is appropriate for a change.
     * @param cards
     * @return
     */
    public boolean isValidExchange(Roster<Card> cards) throws Exception {
        boolean valid = false;


        int earth = 0, water = 0, air = 0, joker = 0;


        for (int i = 0; i
                < cards.count(); i++) {
            int[] typeValues = cards.getElementInPosition(i).getType().getTypeValues();


            for (int j = 0; j
                    < typeValues.length; j++) {
                switch (typeValues[j]) {
                    case (CardType.EARTH):
                        earth++;


                        break;


                    case (CardType.WATER):
                        water++;


                        break;


                    case (CardType.AIR):
                        air++;


                        break;


                    case (CardType.JOKER):
                        joker++;


                        break;


                    default:
                        throw new Exception("No valid type value");


                }
            }
        }
        switch (earth) {
            case (0):
                switch (water) {
                    case (0):
                        switch (air) {
                            case (0):
                                if (joker == 3) {
                                    valid = true;


                                }
                                break;


                            case (1):
                                if (joker == 2) {
                                    valid = true;


                                }
                                break;


                            case (2):
                                if (joker == 1) {
                                    valid = true;


                                }
                                break;


                            case (3):
                                if (joker == 0) {
                                    valid = true;


                                }
                                break;


                        }
                        break;


                    case (1):
                        switch (air) {
                            case (0):
                                if (joker == 2) {
                                    valid = true;


                                }
                                break;


                            case (1):
                                if (joker == 1) {
                                    valid = true;


                                }
                                break;


                        }
                        break;


                    case (2):
                        if (air == 0 && joker == 1) {
                            valid = true;


                        }
                        break;


                    case (3):
                        if (air == 0 && joker == 0) {
                            valid = true;


                        }
                        break;


                }
                break;


            case (1):
                switch (water) {
                    case (0):
                        switch (air) {
                            case (0):
                                if (joker == 2) {
                                    valid = true;


                                }
                                break;


                            case (1):
                                if (joker == 1) {
                                    valid = true;


                                }
                                break;


                        }
                        break;


                    case (1):
                        switch (air) {
                            case (0):
                                if (joker == 1) {
                                    valid = true;


                                }
                                break;


                            case (1):
                                if (joker == 0) {
                                    valid = true;


                                }
                                break;


                        }
                        break;


                }
                break;


            case (2):
                if (water == 0 && air == 0 && joker == 1) {
                    valid = true;


                }
                break;


            case (3):
                if (water == 0 && air == 0 && joker == 0) {
                    valid = true;


                }
                break;


        }
        return valid;


    }

    public final Roster<Country> getCountriesFromContinents(Roster<Continent> continents) {
        Roster<Country> countries = new Roster<Country>();


        for (int i = 0; i
                < continents.count(); i++) {
            Roster<Country> continentCountries = continents.getElementInPosition(i).getCountries();


            for (int j = 0; j
                    < continentCountries.count(); j++) {
                countries.addInOrder(continentCountries.getElementInPosition(j));


            }
        }
        return countries;


    }

    /**
     * the next method should be int he roster class as an override of the clone
     * method. And Roster should implement the Clonable interface.
     *
     */
    public final Roster cloneRoster(Roster original) {
        Roster clon = new Roster();


        for (int i = 0; i
                < original.count(); i++) {
            clon.addLast(original.getElementInPosition(i));


        }
        return clon;


    }

    public void endGame() {
    }//TODO

    public Country drawCowntryCard() {
        int random = (int) (Math.random() * countryCards.count());
        Country country = countryCards.getElementInPosition(random);


        return countryCards.remove(country);


    }

    public Card drawContinentCard(Continent continent) {
        return continentCards.remove(continent);

    }
}
